package com.yilin.tms.message.service.impl;

import com.google.gson.reflect.TypeToken;
import com.sun.istack.NotNull;
import com.yilin.commons.encrypt.EncryptUtils;
import com.yilin.commons.util.JsonUtil;
import com.yilin.commons.util.MapUtil;
import com.yilin.commons.util.StringUtil;
import com.yilin.tms.core.commons.utils.ParamUtil;
import com.yilin.tms.core.entity.message.MessageData;
import com.yilin.tms.core.entity.user.account.Account;
import com.yilin.tms.core.remote.IUserRemote;
import com.yilin.tms.message.service.IMessageService;
import com.yilin.tms.message.service.IPushService;
import com.yilin.tms.message.service.ISocketService;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.socket.*;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;


/**
 * 即时通讯 Socket连接服务
 * 链接地址："ws://$baseUrl/socket/message/data?userId = xxx";
 * 头部附带token:headers{token:xxx}
 */

//WebSocketHandler
@Service
@Transactional
public class SocketService implements WebSocketHandler, ISocketService {

    private final Logger logger = LoggerFactory.getLogger(SocketService.class);

    private static final String connectionCheck = "1";//连接反馈 心跳包
    private static final String userOnlineCheck = "checkUser:";//用户在线检查
    private static final String sendReturnBack = "sendBack:";//发送成功回传
    //TODO:保存用户链接
    private static ConcurrentHashMap<String, WebSocketSession> users = new ConcurrentHashMap<>();

    @Resource
    private IPushService pushService;
    @Resource
    private IMessageService messageService;
    @Resource
    private IUserRemote userRemote;

    /**
     * 连接就绪
     */
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        String userId = (String) session.getAttributes().get("HTTP.SESSION.userId");
        //不为空则遍历，空则直接添加
        if (!users.isEmpty()) {
            WebSocketSession socketSession = getOpenUser(userId);
            if (socketSession != null && socketSession.isOpen()) {
                socketSession.close();
                users.remove(userId);
            }
        }
        users.put(userId, session);
        logger.info(session.getId() + ">>>> 用户：" + userId + " -> 连接成功");
        sendMessageToUser(session, new TextMessage(connectionCheck));
        ///上线时，接收离线消息并为对方处理为发送成功
        List<MessageData> messageDataList = messageService.getFailChatListByReceiveUserId(userId);
        sendMessageListToUser(messageDataList);
    }

    /**
     * 接收聊天信息
     */
    @Override
    public void handleMessage(@NotNull WebSocketSession session, WebSocketMessage<?> message) {
        if (StringUtil.isEmpty((String) message.getPayload())) return;
        String userId = (String) session.getAttributes().get("HTTP.SESSION.userId");
        String token = (String) session.getAttributes().get("HTTP.SESSION.token");
        if (message.getPayload().equals(connectionCheck)) {//心跳检查
            sendMessageToUser(getOpenUser(userId), new TextMessage(connectionCheck));
            return;
        }
        if (((String) message.getPayload()).contains(userOnlineCheck)) {//检查用户在线
            String[] split = ((String) message.getPayload()).split(userOnlineCheck);
            if (split.length < 2) return;
            sendMessageToUser(getOpenUser(userId), new TextMessage(userOnlineCheck + checkOpenUser(split[1])));
            return;
        }
        ///解密数据
        String decryptJson = EncryptUtils.aesDecrypt((String) message.getPayload(), token);
        //转成对象
        MessageData data = JsonUtil.jsonToObject(decryptJson, new TypeToken<MessageData>() {
        });
        if (data == null) return;
        //发送聊天消息给对方
        sendMessageToUser(data);
        ///发送成功回传
        TextMessage message1 = new TextMessage(sendReturnBack + data.getsId());
        sendMessageToUser(session, message1);
    }

    /**
     * 发送聊天消息
     */
    @Override
    public void sendMessageToUser(MessageData messageData) {
        try {
            WebSocketSession toUserSession = getOpenUser(messageData.getToAccountId());
            if (toUserSession == null) {
                //对方不在线 推送离线消息 接收失败
                messageData.setSendState(MessageData.SendState.fail);
                messageService.saveOrUpdate(messageData);
                String[] alias = new String[]{messageData.getToAccountId()};
                Map<String, Object> generate = MapUtil.generate(map -> map.put("targetUserId", messageData.getAccountId()));
                Account account= ParamUtil.isSuccess(userRemote.getAccountById(messageData.getToAccountId()));
                pushService.sendMessageByAlias(account.getSuperiorPlatId(),messageData.getToAccountUserType(), alias, messageData.getAccountName(), messageData.getText(), generate);
                return;
            }
            //对方在线 获取对方Token
            String toToken = (String) toUserSession.getAttributes().get("HTTP.SESSION.token");
            //用对方的Token加密数据
            String toEncryptJson = EncryptUtils.aesEncrypt(JsonUtil.objectToJson(messageData), toToken);
            if (toEncryptJson == null) return;
            ///发送JSON数据给对方
            String message = JsonUtil.objectToJson(MapUtil.generate(map -> map.put("message", toEncryptJson)));
            sendMessageToUser(toUserSession, new TextMessage(message));
            //给对方,接收成功
            messageData.setSendState(MessageData.SendState.receive);
            messageService.saveOrUpdate(messageData);
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
    }

    /**
     * 批量发送消息
     */
    @Override
    public void sendMessageListToUser(List<MessageData> messageDataList) {
        if (messageDataList.size() == 0) return;
        MessageData endMessageData = messageDataList.get(0);
        WebSocketSession toUserSession = getOpenUser(endMessageData.getToAccountId());
        if (toUserSession == null) return;
        //获取对方数据，转发给对方,接收成功
        String toToken = (String) toUserSession.getAttributes().get("HTTP.SESSION.token");
        //用对方的Token加密数据
        String toEncryptJson = EncryptUtils.aesEncrypt(JsonUtil.objectToJson(messageDataList), toToken);
        if (toEncryptJson == null) return;
        ///发送JSON数据给对方
        Map<String, Object> messageList = MapUtil.generate(map -> map.put("messageList", toEncryptJson));
        sendMessageToUser(toUserSession, new TextMessage(JsonUtil.objectToJson(messageList)));
        ///接收成功
        for (MessageData messageData : messageDataList) {
            messageData.setSendState(MessageData.SendState.receive);
            messageService.saveOrUpdate(messageData);
        }
    }

    /**
     * 用户关闭连接离线时
     */
    @Override
    public void afterConnectionClosed(WebSocketSession session, @NotNull CloseStatus closeStatus) {
        String userId = (String) session.getAttributes().get("HTTP.SESSION.userId");
        users.remove(userId);
        logger.info(session.getId() + ">>>> 用户：" + userId + " -> 已断开");
    }

    /**
     * 公共
     * 获取某个用户是否在线
     */
    @Override
    public void sendMessageToUser(WebSocketSession session, TextMessage message) {
        if (session == null) return;
        try {
            if (session.isOpen()) {
                session.sendMessage(message);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 公共
     * 给所有在线用户发送消息
     */
    @Override
    public void sendMessageToAllUser(WebSocketMessage<?> message) {
        for (WebSocketSession user : users.values()) {
            try {
                if (user.isOpen()) {
                    user.sendMessage(message);
                    System.out.println(user + "成功：给所有在线用户发送消息");
                }
            } catch (IOException e) {
                System.out.println("失败：给所有在线用户发送消息");
                e.printStackTrace();
            }
        }
    }

    /**
     * 检查用户是否在线
     */
    @Override
    public boolean checkOpenUser(String userId) {
        return getOpenUser(userId) != null;
    }

    @Override
    public WebSocketSession getOpenUser(String userId) {
        for (ConcurrentHashMap.Entry<String, WebSocketSession> user : users.entrySet()) {
            if (user.getKey().equals(userId)) {
                return user.getValue();
            }
        }
        return null;
    }

    //传输异常
    @Override
    public void handleTransportError(WebSocketSession session, Throwable exception) {
        logger.info(session.getId() + ">>>> SOCKET传输异常：" + session.getId() + " -> " + exception.getMessage());
    }

    //是否支持分包
    @Override
    public boolean supportsPartialMessages() {
        return false;
    }

}
